/*
 * @Descripttion: 
 * @Author: wayde
 * @Date: 2021-10-30 17:17:10
 */
const config = require('./config.js');
const http = require('http')
const zlib = require('zlib')
const chalk = require('chalk');
const path = require('path');
const url = require('url');
const {inspect,promisify} = require('util');
const fs = require('fs');
const stat = promisify(fs.stat);
const mime = require('mime');
// https://www.handlebarsjs.cn/guide/
const debug = require("debug");



class Server{
    constructor(options){
        this.config = Object.assign({},config,options)
    }
    start(){
    //    =====http 服务====
        let server = http.createServer()
        server.on('request',this.request.bind(this))
        server.listen(this.config.port,()=>{
            let url = `${this.config.host}:${this.config.port}`;
            debug(`server started at ${chalk.green(url)}`);
        });
    }
    async request(req,res){
        /* === 处理客户端请求，决定响应信息 === */
        // try
        //如果是文件夹 -> 显示子文件、文件夹列表
        //如果是文件 -> sendFile()
        // catch
        //出错 -> sendError()
        let{pathname} = url.parse(req.url);
        if(pathname === '/favicon.ico') return res.end(); //网站图标
        let filepath = path.join(this.config.root,pathname);
        try{
            let statObj = await stat(filepath);
            console.log(filepath,statObj)
            this.sendFile(req,res,filepath,statObj);
        }catch(e){
            console.log(e)
            debug(inspect(e)); //inspect把一个toString后的对象仍然能展开显示
            this.sendError(req,res);
        }

    }
    sendFile(req,res,filePath,statObj){
        //对要返回的文件进行预处理并发送文件
        if(this.handleCache(req,res,filePath,statObj))return; //如果走缓存，则直接返回
        res.setHeader('Content-type',mime.getType(filePath));
        let encoding = this.getEncoding(req,res);
        let rs =  fs.createReadStream(filePath);
        if(encoding){
            console.log(res)
            rs.pipe(encoding).pipe(res); //先交给转换流（encoding）处理，再分发给res
        }else{
            rs.pipe(res);
        }
    }
    handleCache(req,res,filepath,statObj){
        //获取和设置缓存相关信息
        // 区分强缓存和 协商缓存 两者配合使用减少服务器压力
        let ifModifiedSince = req.headers['if-modified-since'];
        let isNoneMatch = req.headers['is-none-match'];
        res.setHeader('Cache-Control','private,max-age=30');
        res.setHeader('Expires',new Date(Date.now()+30*1000).toGMTString()); //这个expires的强制缓存的时间格式不能随便写
        let etag = statObj.size; //new Date(statObj.ctime.toGMTString()).getTime()+'-'+statObj.size
        let lastModified = statObj.ctime.toGMTString();
        res.setHeader('ETag',etag);
        res.setHeader('Last-Modified',lastModified);

        if(isNoneMatch && isNoneMatch != etag)return false; //存在且相等就放行
        if(ifModifiedSince && ifModifiedSince != lastModified)return false;
        if(isNoneMatch || ifModifiedSince){
            res.writeHead(304);
            res.end();
            return true;
        }else{
            return false;
        }
    }
    getEncoding(req,res){
        //获取和设置编码相关信息
        const acceptEncoding = req.headers['accept-encoding']
        if(/\bgzip\b/.test(acceptEncoding)){
            res.setHeader('Content-Encoding','gzip');
            return zlib.createGzip();
        }else if(/\bdeflate\b/.test(acceptEncoding)){
            res.setHeader('Content-Encoding','deflate');
            return zlib.createDeflate();
        }else{
            return null;
        }
    }
    sendError(req,res){
        //错误提示
        res.statusCode = 500;
        res.end(`there is something wrong in the server! please try later!`);
    }
}
module.exports = Server;